package com.hyt.it.ogt.kq.service.gov.arrange;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import com.alibaba.excel.EasyExcel;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.core.base.BaseEntity;
import com.hyt.core.constant.CommonConstant;
import com.hyt.core.util.UUIDUtils;
import com.hyt.exception.exception.BusinessLogicException;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.gov.constant.ArrangeParamCodeEnum;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.common.gov.constant.StringCodeEnum;
import com.hyt.it.ogt.kq.common.gov.utils.StringUtil;
import com.hyt.it.ogt.kq.service.gov.feign.ks.KsClient;
import com.hyt.it.ogt.kq.service.gov.feign.ks.model.PushKsSubjectParam;
import com.hyt.it.ogt.kq.service.gov.feign.ks.model.PushTimeSubjectParam;
import com.hyt.it.ogt.kq.service.gov.model.entity.TaskRoom;
import com.hyt.it.ogt.kq.service.gov.model.entity.Time;
import com.hyt.it.ogt.kq.service.gov.model.entity.TimeRoom;
import com.hyt.it.ogt.kq.service.gov.model.entity.TimeRoomCandidate;
import com.hyt.it.ogt.kq.service.gov.model.entity.TimeSubject;
import com.hyt.it.ogt.kq.service.gov.model.excel.ExcelErr;
import com.hyt.it.ogt.kq.service.gov.model.excel.ExcelLineErr;
import com.hyt.it.ogt.kq.service.gov.model.excel.ExcelRowErr;
import com.hyt.it.ogt.kq.service.gov.model.excel.SelfArrangeSheet;
import com.hyt.it.ogt.kq.service.gov.model.excel.listener.SelfArrangeListener;
import com.hyt.it.ogt.kq.service.gov.model.param.ArrangeParam;
import com.hyt.it.ogt.kq.service.gov.service.ISubjectService;
import com.hyt.it.ogt.kq.service.model.entity.Candidate;
import com.hyt.it.ogt.kq.service.model.entity.CandidateSubject;
import com.hyt.it.ogt.kq.service.model.entity.Subject;
import com.hyt.progress.service.ProgressManager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 自定义编排
 */
@Slf4j
@Component
public class ImportArrangeAdapter extends ArrangeAbstractService {

    @Resource
    ISubjectService iSubjectService;
    @Resource
    private KsClient ksClient;

    @Override
    public Object arrange(ArrangeParam param) {
        this.check(param);

        String fileSuffix = FileUtil.getSuffix(param.getFileUrl());
        if (!StrUtil.equalsAny(fileSuffix, "xlsx", "xls")) {
            BusinessLogicException.throwException(43330403, "上传的文件类型不匹配");
        }
        File uploadFile = FileUtil.createTempFile(StrPool.DOT + fileSuffix, Boolean.TRUE);
        long fileSize = HttpUtil.downloadFile(param.getFileUrl(), uploadFile);
        if (fileSize == CommonConstant.ZERO) {
            BusinessLogicException.throwException(43330402, "请勿上传一个空文件");
        }
        ProgressManager.updateProgressCurrent(10L, "文件接收成功");

        // 异常信息记录
        List<ExcelErr> errList = new ArrayList<>();
        ProgressManager.updateProgressCurrent(15L, "Excel文件格式校验完成");

        // 读取excel
        List<SelfArrangeSheet> selfArrangeSheets = this.readExcel(uploadFile);
        ProgressManager.updateProgressCurrent(20L, "Excel文件读取完成");

        if (CollectionUtils.isEmpty(selfArrangeSheets)) {
            throw new KqException(ResponseCode.ARRANGE_IMPORT_EXCEL_NULL.getCode(), ResponseCode.ARRANGE_IMPORT_EXCEL_NULL.getMsg());
        }

        // 实际编排数据记录
        List<TimeRoomCandidate> arrangeDataList = new ArrayList<>();
        // 记录批次科目关系
        List<TimeSubject> timeSubjectList = new ArrayList<>();
        List<Time> timeList = iTimeService.getByTaskId(param.getTaskId());
        Map<String, Subject> subjectMap = iSubjectService.getSubjectMapKeyId(param.getTaskId());
        Map<String, String> timeRoomIdMap = new HashMap<>();
        List<TaskRoom> roomList = iTaskRoomService.lambdaQuery().eq(TaskRoom::getTaskId, param.getTaskId()).eq(TaskRoom::getDelFlag, false).list();
        Map<String,List<String>> timeSubjectListMap = new HashMap<>();
        //这里构造map是为何timeRoom表的id与考生编排表的timeRoomId保持一致
        for(Time time : timeList){
            for (TaskRoom taskRoom : roomList) {
                String timeRoomId = UUIDUtils.newSortUUID();
                timeRoomIdMap.put(time.getId()+"_"+taskRoom.getId(), timeRoomId);
            }
        }

        // 一个sheet异常记录
        ExcelErr sheetErr = ExcelErr.builder().sheetNo(0).sheetName("自定义编排").build();
        List<ExcelRowErr> row3Errs = this.analysisExcelData(param, selfArrangeSheets,timeList , roomList,
                arrangeDataList, timeSubjectList,timeRoomIdMap,timeSubjectListMap);
        if(row3Errs.size() > 0) {
            // 导入有异常
            sheetErr.setRowErrList(row3Errs);
            errList.add(sheetErr);
        }

        // 删除编排表数据，后面重新录入
        iTimeRoomCandidateService.lambdaUpdate().set(BaseEntity::getDelFlag, true).eq(TimeRoomCandidate::getTaskId, param.getTaskId()).eq(BaseEntity::getDelFlag, false).update();

        if (CollUtil.isNotEmpty(errList)) {
            ProgressManager.updateProgressCurrent(99L, "数据校验完成");
            return errList;
        } else {
            ProgressManager.updateProgressCurrent(90L, "数据保存中");
            if(arrangeDataList.size() > 0) {
                // 保存编排数据
                iTimeRoomCandidateService.saveBatch(arrangeDataList, 1000);
                // 保存科目批次数据
                iTimeSubjectService.saveBatch(timeSubjectList, 1000);
                //推送批次科目数据
                pushTimeSubject(timeSubjectListMap,subjectMap,param.getTaskId());
            }
            // 初始化场次数据
            super.initTimeRoom(param, arrangeDataList,timeRoomIdMap);
        }
        return null;
    }

    private void pushTimeSubject(Map<String,List<String>> timeSubjectListMap,Map<String, Subject> subjectMap,String taskId){
        for (Map.Entry<String, List<String>> entry : timeSubjectListMap.entrySet()) {
            List<PushKsSubjectParam> subjects = new ArrayList<>();
            List<String> subjectList = entry.getValue();
            if(CollectionUtil.isNotEmpty(subjectList)){
                for(String subjectId : subjectList){
                    PushKsSubjectParam pushSubject = new PushKsSubjectParam();
                    Subject subject = subjectMap.get(subjectId);
                    pushSubject.setSubjectName(subject.getName());
                    if(Objects.nonNull(subject.getBmSubjectId())){
                        pushSubject.setSubjectId(subject.getBmSubjectId());
                    } else {
                        pushSubject.setSubjectId(subject.getId());
                    }
                    pushSubject.setOfficeId(subject.getOfficeId());
                    pushSubject.setDeptId(subject.getDeptId());
                    subjects.add(pushSubject);
                }
            }

            PushTimeSubjectParam subjectParam = new PushTimeSubjectParam();
            subjectParam.setTimeId(entry.getKey());
            subjectParam.setTaskId(taskId);
            subjectParam.setSubjects(subjects);
            ApiResponse<Object> response = ksClient.pushTimeSubject(subjectParam);
            if(!Objects.equals(ResponseCode.SUCCESS.getCode(),response.getCode())){
                throw new KqException(ResponseCode.PUSH_TIME_SUBJECT_ERROR);
            }

        }
    }

    private List<ExcelRowErr> analysisExcelData(ArrangeParam param, List<SelfArrangeSheet> sheetDataList,List<Time> timeList,List<TaskRoom> roomList, List<TimeRoomCandidate> arrangeDataList, List<TimeSubject> timeSubjectList,Map<String, String> timeRoomIdMap,Map<String,List<String>> timeSubjectListMap) {
        String taskId = param.getTaskId();
        List<ExcelRowErr> rowErrs = new ArrayList<>();

        // 批次科目不能有
        Map<String, String> subjectMap = iTimeSubjectService.getSubjectMap(taskId);

        Map<String, String> timeMap = new HashMap<>();
        for (Time time : timeList) {
            timeMap.put(String.valueOf(time.getCode()), time.getId());
            timeSubjectListMap.put(time.getId(),new ArrayList<>());
        }
        Map<String, TaskRoom> roomMap = new HashMap<>();
        for (TaskRoom taskRoom : roomList) {
            roomMap.put(String.valueOf(taskRoom.getCode()), taskRoom);
        }

        int totalSize = sheetDataList.size();
        // 判断导入的excel数是否是空表格
        if(totalSize == 0) {
            throw new BusinessLogicException(ResponseCode.ARRANGE_IMPORT_TIME_ROOM_NOT_ACCEPT.getCode(), ResponseCode.ARRANGE_IMPORT_TIME_ROOM_NOT_ACCEPT.getMsg());
        }

        //2.获取编排参数，并且格式化
        Map<String, String> paramMap = iTimeRoomParamService.getTimeRoomParamMap(param.getTimeRoomParamVOList(), taskId);

        // 默认设置，如果其考生没有设置自定义准考证号，则生成系统一个准考证号，允许导入的excel中部分考生有准考证号部分没有
        paramMap.put(StringCodeEnum.ARRANGE_PARAM_ADMISSION_TYPE.getCode(), String.valueOf(ArrangeParamCodeEnum.ADMISSION_TYPE_RANDOM.getCode()));

        Map<String, String> admissionNumberIDCardMap = new HashMap<>();
        Map<String, String> timeSubjectMap = new HashMap<>();
        Map<String, Integer> timeRoomMap = new HashMap<>();
        Map<String, String> timeCandidateMap = new HashMap<>();
        Map<String,String> subjectTimeMap = new HashMap<>();

        // 查询所有考生科目映射关系
        List<CandidateSubject> candidateSubjectList = iCandidateSubjectService.lambdaQuery().eq(CandidateSubject::getTaskId, taskId)
                .eq(CandidateSubject::getDelFlag, false).list();
        // 构建考生+科目关系，编排表需要插入该字段
        Map<String, String> candidateSubjectMap = new HashMap<>();
        for (CandidateSubject candidateSubject : candidateSubjectList) {
            candidateSubjectMap.put(candidateSubject.getCandidateId() + "_" + candidateSubject.getSubjectId(), candidateSubject.getId());
        }

        for (int i = 0; i < sheetDataList.size(); i++) {
            SelfArrangeSheet row = sheetDataList.get(i);

            String result = StrUtil.format("正在校验Excel文件，共计{}条数据，正在校验第{}条数据", totalSize, i);
            ProgressManager.stepProgressCurrent(i, totalSize, 30L, 90L, result);

            TimeRoomCandidate timeRoomCandidate = new TimeRoomCandidate();

            // 校验excel每一行表格数据
            List<ExcelLineErr> lineErrs = this.checkRow(row, taskId, subjectTimeMap, timeMap, subjectMap, roomMap, candidateSubjectMap, admissionNumberIDCardMap, timeCandidateMap);
            if(lineErrs.size() > 0) {
                rowErrs.add(ExcelRowErr.builder().row(i+2).lineErrList(lineErrs).build());
                continue;
            }
            if(StrUtil.isNotBlank(row.getRoomCode())){
                TaskRoom taskRoom = roomMap.get(row.getRoomCode());

                int num = 0;
                if(ObjectUtil.isNotEmpty(timeRoomMap)){
                    if(timeRoomMap.containsKey(timeMap.get(row.getTimeCode())+"#"+taskRoom.getId())){
                        num =  timeRoomMap.get(timeMap.get(row.getTimeCode())+"#"+taskRoom.getId());
                    } else {
                        num = taskRoom.getSeatNum();
                    }
                } else{
                    num = taskRoom.getSeatNum();
                }
                if(num > 0) {
                    timeRoomCandidate.setId(UUIDUtils.randomUUID());
                    timeRoomCandidate.setTaskId(taskId);
                    timeRoomCandidate.setTimeId(timeMap.get(row.getTimeCode()));
                    timeRoomCandidate.setPlaceId(iTaskPlaceService.getById(taskRoom.getPlaceId()).getId());
                    timeRoomCandidate.setRoomId(taskRoom.getId());
                    timeRoomCandidate.setCandidateId(row.getCandidateId());
                    String timeRoomId = StrUtil.isNotBlank(timeRoomIdMap.get(timeRoomCandidate.getTimeId()+"_"+taskRoom.getId())) ? timeRoomIdMap.get(timeRoomCandidate.getTimeId()+"_"+taskRoom.getId()) : UUIDUtils.newSortUUID();
                    timeRoomCandidate.setTimeRoomId(timeRoomId);
                    timeRoomCandidate.setSubjectId(subjectMap.get(row.getSubject()));
                    timeRoomCandidate.setCandidateSubjectId(row.getCandidateSubjectId());
                    timeRoomCandidate.setSeatNum(StringUtil.stringFormatZero(Integer.valueOf(row.getSeat()), 3));
                    arrangeDataList.add(timeRoomCandidate);
                    num--;

                    timeRoomMap.put(timeMap.get(row.getTimeCode())+"#"+taskRoom.getId(),num);


                }
                String timeSubjectKey = timeRoomCandidate.getTimeId() + timeRoomCandidate.getSubjectId();
                if(!timeSubjectMap.containsKey(timeSubjectKey)) {
                    // 保存批次科目绑定关系
                    TimeSubject timeSubject = new TimeSubject();
                    timeSubject.setTaskId(taskId).setSubjectId(timeRoomCandidate.getSubjectId()).setTimeId(timeRoomCandidate.getTimeId()).setId(UUIDUtils.randomUUID());
                    timeSubjectList.add(timeSubject);
                }
                if(timeSubjectListMap.containsKey(timeRoomCandidate.getTimeId())){
                    List<String> subjectList = timeSubjectListMap.get(timeRoomCandidate.getTimeId());
                    if (CollectionUtil.isEmpty(subjectList)) {
                        subjectList = new ArrayList<>();
                        subjectList.add(timeRoomCandidate.getSubjectId());
                        timeSubjectListMap.put(timeRoomCandidate.getTimeId(),subjectList);
                    } else {
                        if (!subjectList.contains(timeRoomCandidate.getSubjectId())) {
                            subjectList.add(timeRoomCandidate.getSubjectId());
                        }
                    }
                }
                // 默认考生多科目使用一个准考证号
                String admissionNumber = null;

                if(StringUtils.isBlank(row.getAdmissionCode())) {
                    // 默认使用系统随机生成准考证号
                    admissionNumber = admissionService.getAdmissionNumber(taskId, paramMap, null);
                } else {
                    // 默认使用导入的自定义准考证号
                    admissionNumber = row.getAdmissionCode();
                }
                timeRoomCandidate.setAdmissionNum(admissionNumber);
            }
        }
        return rowErrs;
    }

    /**
     * 校验excel每一行数据
     * @param row excel数据行
     * @param taskId 任务ID
     * @param timeMap 所有批次
     * @param subjectMap 所有科目
     * @param roomMap 所有考场
     * @param candidateSubjectMap 考生科目映射关系
     * @param admissionNumberIDCardMap 记录准考证号-证件号映射关系，判断不同考生是否使用相同准考证号，临时使用
     * @param timeCandidateMap 记录考生批次映射关系，判断考生是否多个科目报考同一个批次，临时使用
     * @return
     */
    private List<ExcelLineErr> checkRow(SelfArrangeSheet row, String taskId, Map<String,String> subjectTimeMap, Map<String, String> timeMap,
                                        Map<String, String> subjectMap, Map<String, TaskRoom> roomMap,
                                        Map<String, String> candidateSubjectMap, Map<String, String> admissionNumberIDCardMap,
                                        Map<String, String> timeCandidateMap) {
        List<ExcelLineErr> lineErrs = new ArrayList<>();
        //由于读取excel 会读取空白行，需要过滤空白行
        if(StrUtil.isBlank(row.getName()) && StrUtil.isBlank(row.getCardType())
                && StrUtil.isBlank(row.getCardNumber()) && StrUtil.isBlank(row.getSubject())
                && StrUtil.isBlank(row.getTimeCode()) && StrUtil.isBlank(row.getSeat())
                && StrUtil.isBlank(row.getRoomCode()) && StrUtil.isBlank(row.getAdmissionCode())){
            return lineErrs;
        }
        if (StringUtils.isEmpty(row.getName())) {
            lineErrs.add(ExcelLineErr.builder().line(1).msg("姓名不能为空").build());
        }
        if (StringUtils.isEmpty(row.getCardType())) {
            lineErrs.add(ExcelLineErr.builder().line(2).msg("证件类型不能为空").build());
        }
        // 查询考生
        Candidate candidate = iCandidateService.lambdaQuery().select(BaseEntity::getId).eq(Candidate::getTaskId, taskId)
                .eq(Candidate::getIdNumber, row.getCardNumber()).eq(BaseEntity::getDelFlag, false).one();

        if (StringUtils.isEmpty(row.getCardNumber())) {
            lineErrs.add(ExcelLineErr.builder().line(3).msg("证件号码不能为空").build());
        } else {
            // 考生是否存在
            if(Objects.isNull(candidate)) {
                lineErrs.add(ExcelLineErr.builder().line(3).msg("【"+row.getCardNumber()+"】证件号码在系统中不存在").build());
            }else{
                row.setCandidateId(candidate.getId());
            }
        }

        String timeId = timeMap.get(row.getTimeCode());
        if (StringUtils.isEmpty(row.getSubject())) {
            lineErrs.add(ExcelLineErr.builder().line(4).msg("报考科目不能为空").build());
        } else {
            if (candidate != null) {
                String subjectId = subjectMap.get(row.getSubject());
                String candidateSubjectId = candidateSubjectMap.get(candidate.
                        getId() + "_" + subjectId);
                // 科目是否存在
                if(Objects.isNull(subjectId)) {
                    lineErrs.add(ExcelLineErr.builder().line(4).msg("报考科目在系统中不存在").build());
                } else if(Objects.isNull(candidateSubjectId)){
                    lineErrs.add(ExcelLineErr.builder().line(4).msg("考生未报考该科目").build());
                } else {
                    if (StringUtils.isNotEmpty(timeId) && Objects.nonNull(candidate)) {
                        String timeCandidateKey = timeId + "_" + candidate.getId();
                        if(timeCandidateMap.containsKey(timeCandidateKey)) {
                            lineErrs.add(ExcelLineErr.builder().line(4).msg("该考生不能在一个批次中报考多个科目").build());
                        } else {
                            timeCandidateMap.put(timeCandidateKey, candidate.getId());
                        }

                        if(subjectTimeMap.containsKey(subjectId)){
                            if(!timeId.equals(subjectTimeMap.get(subjectId))){
                                lineErrs.add(ExcelLineErr.builder().line(4).msg("一个科目中不允许存在多个批次").build());
                            }
                        } else {
                            subjectTimeMap.put(subjectId,timeId);
                        }

                    }

                }



            }
        }

        if (StringUtils.isNotEmpty(row.getAdmissionCode())) {
            String idCard = admissionNumberIDCardMap.get("zkz_" + row.getAdmissionCode());
            if(StringUtils.isNotBlank(idCard)) {
                if(!row.getCardNumber().equalsIgnoreCase(idCard)) { // 不是同一个考生准考证号不能定义重复
                    lineErrs.add(ExcelLineErr.builder().line(5).msg("自定义准考证号重复").build());
                }
            }
            admissionNumberIDCardMap.put("zkz_" + row.getAdmissionCode(), row.getCardNumber());
        }
        if (StringUtils.isEmpty(row.getRoomCode())) {
            lineErrs.add(ExcelLineErr.builder().line(6).msg("考场编码不能为空").build());
        } else {
            // 考场是否存在
            if(!roomMap.containsKey(row.getRoomCode())) {
                lineErrs.add(ExcelLineErr.builder().line(6).msg("考场编码在系统中不存在").build());
            }
        }

        if (StringUtils.isEmpty(row.getSeat())) {
            lineErrs.add(ExcelLineErr.builder().line(7).msg("座位号不能为空").build());
        } else {
            if(!NumberUtil.isInteger(row.getSeat())) {
                lineErrs.add(ExcelLineErr.builder().line(7).msg("座位号必须是正整数").build());
            }
            // 同一批次考场，不能存在同一座位号
            String zwhKey = "zwh_" + row.getSeat() + "#" + row.getTimeCode() + "#" + row.getRoomCode();
            if(admissionNumberIDCardMap.containsKey(zwhKey)) {
                lineErrs.add(ExcelLineErr.builder().line(7).msg("座位号重复").build());
            }
            admissionNumberIDCardMap.put(zwhKey, "");
        }

        if (StringUtils.isEmpty(row.getTimeCode())) {
            lineErrs.add(ExcelLineErr.builder().line(8).msg("批次编码不能为空").build());
        } else {
            // 批次是否存在
            if(StringUtils.isEmpty(timeId)) {
                lineErrs.add(ExcelLineErr.builder().line(8).msg("批次编码在系统中不存在").build());
            }
        }
        return lineErrs;
    }

    private List<SelfArrangeSheet> readExcel(File file) {
        SelfArrangeListener listener = new SelfArrangeListener();
        EasyExcel.read(file, SelfArrangeSheet.class, listener).sheet(0).doRead();
        return listener.getDataList();
    }

    private void check(ArrangeParam param) {
        // 判断批次是否绑定有科目，只要有一个批次绑定有一个科目即可
        if(iTimeSubjectService.lambdaQuery().eq(TimeSubject::getTaskId, param.getTaskId()).isNotNull(TimeSubject::getSubjectId).eq(BaseEntity::getDelFlag, false).count() > 0) {
            throw new BusinessLogicException(ResponseCode.ARRANGE_IMPORT_TIME_SUBJECT_NOT_ACCEPT.getCode(), ResponseCode.ARRANGE_IMPORT_TIME_SUBJECT_NOT_ACCEPT.getMsg());
        }

        // 批次免考场不能有
        if(iTimeRoomService.lambdaQuery().eq(TimeRoom::getTaskId, param.getTaskId()).eq(BaseEntity::getDelFlag, false).count() > 0) {
            throw new BusinessLogicException(ResponseCode.ARRANGE_IMPORT_TIME_ROOM_NOT_ACCEPT.getCode(), ResponseCode.ARRANGE_IMPORT_TIME_ROOM_NOT_ACCEPT.getMsg());
        }

        // 没有实体考场数据，不能进行实体考场编排
        Integer count = iTaskRoomService.lambdaQuery().eq(TaskRoom::getTaskId, param.getTaskId()).eq(BaseEntity::getDelFlag, false).count();
        if (count == 0) {
            throw new KqException(ResponseCode.ARRANGE_ROOM_NULL.getCode(), ResponseCode.ARRANGE_ROOM_NULL.getMsg());
        }

    }
}
